import os
import json
import sqlite3
from datetime import datetime, timezone, timedelta
from typing import List, Dict, Any

from flask import Flask, request, jsonify, send_from_directory

from services.preprocessing import filter_signal, segment_window, normalize
from services.model import evaluate_features
from services.recommendation import generate_plan

DB_PATH = os.path.join(os.path.dirname(__file__), 'db.sqlite3')

app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), 'static'))


def init_db():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute(
        """
        CREATE TABLE IF NOT EXISTS evaluations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            score REAL NOT NULL,
            anomaly_prob REAL NOT NULL
        )
        """
    )
    c.execute(
        """
        CREATE TABLE IF NOT EXISTS plans (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            plan_json TEXT NOT NULL
        )
        """
    )
    # 新增反馈表
    c.execute(
        """
        CREATE TABLE IF NOT EXISTS feedbacks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            adherence INTEGER,
            pain INTEGER,
            duration_minutes INTEGER,
            note TEXT
        )
        """
    )
    conn.commit()
    conn.close()


@app.route('/')
def index():
    return send_from_directory(app.static_folder, 'home.html')

@app.route('/patient')
def page_patient():
    return send_from_directory(app.static_folder, 'index.html')

@app.route('/doctor')
def page_doctor():
    return send_from_directory(app.static_folder, 'doctor.html')


@app.route('/api/evaluations', methods=['GET'])
def api_evaluations():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT timestamp, score, anomaly_prob FROM evaluations ORDER BY id DESC LIMIT 100")
    rows = c.fetchall()
    conn.close()
    data = [
        {
            'timestamp': ts,
            'score': float(score),
            'anomaly_prob': float(ap)
        } for ts, score, ap in reversed(rows)
    ]
    return jsonify({'items': data})


@app.route('/api/plan', methods=['GET'])
def api_plan():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT timestamp, plan_json FROM plans ORDER BY id DESC LIMIT 1")
    row = c.fetchone()
    conn.close()
    if row:
        ts, plan_json = row
        return jsonify({'timestamp': ts, 'plan': json.loads(plan_json)})
    return jsonify({'message': 'no plan yet'}), 404


@app.route('/api/receive_sensor_data', methods=['POST'])
def receive_sensor_data():
    payload = request.get_json(force=True)
    # Expected keys: imu (list of dicts), pressure (list of floats), fs (sampling freq)
    imu_samples: List[Dict[str, Any]] = payload.get('imu', [])
    pressure_samples: List[float] = payload.get('pressure', [])
    fs: float = float(payload.get('fs', 100))

    # Flatten IMU arrays: assume imu_samples[i] has 'acc', 'gyro', 'mag' each 3 floats
    # Build per-channel arrays for filtering
    acc_x, acc_y, acc_z = [], [], []
    gyro_x, gyro_y, gyro_z = [], [], []
    mag_x, mag_y, mag_z = [], [], []

    for s in imu_samples:
        acc_vals = s.get('acc') or [0, 0, 0]
        gyro_vals = s.get('gyro') or [0, 0, 0]
        mag_vals = s.get('mag') or [0, 0, 0]
        ax, ay, az = acc_vals
        gx, gy, gz = gyro_vals
        mx, my, mz = mag_vals
        acc_x.append(ax); acc_y.append(ay); acc_z.append(az)
        gyro_x.append(gx); gyro_y.append(gy); gyro_z.append(gz)
        mag_x.append(mx); mag_y.append(my); mag_z.append(mz)

    # Filter signals (Butterworth low-pass)
    acc_x_f = filter_signal(acc_x, fs)
    acc_y_f = filter_signal(acc_y, fs)
    acc_z_f = filter_signal(acc_z, fs)
    gyro_x_f = filter_signal(gyro_x, fs)
    gyro_y_f = filter_signal(gyro_y, fs)
    gyro_z_f = filter_signal(gyro_z, fs)
    mag_x_f = filter_signal(mag_x, fs)
    mag_y_f = filter_signal(mag_y, fs)
    mag_z_f = filter_signal(mag_z, fs)
    pressure_f = filter_signal(pressure_samples, fs)

    # Stack channels into multivariate windowed segments
    # Shape per sample: [acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z, mag_x, mag_y, mag_z, pressure]
    multivar = list(zip(acc_x_f, acc_y_f, acc_z_f,
                        gyro_x_f, gyro_y_f, gyro_z_f,
                        mag_x_f, mag_y_f, mag_z_f,
                        pressure_f))

    windows = segment_window(multivar, window_size=int(fs*2), step=int(fs))
    features = [normalize(w) for w in windows]

    # Evaluate via model stub (TCN-Transformer placeholder)
    score, anomaly_prob = evaluate_features(features)

    # Persist evaluation
    ts = datetime.now(timezone.utc).isoformat()
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("INSERT INTO evaluations (timestamp, score, anomaly_prob) VALUES (?, ?, ?)", (ts, score, anomaly_prob))

    # Generate plan (DQN stub)
    plan = generate_plan(score, anomaly_prob)
    c.execute("INSERT INTO plans (timestamp, plan_json) VALUES (?, ?)", (ts, json.dumps(plan)))
    conn.commit()
    conn.close()

    return jsonify({
        'timestamp': ts,
        'score': score,
        'anomaly_prob': anomaly_prob,
        'plan': plan
    })


@app.route('/api/training_feedback', methods=['POST'])
def training_feedback():
    # Accept feedback metrics like adherence, pain_level, completion_time
    payload = request.get_json(force=True)
    # In a real system, this would update the DQN rewards and re-optimize the policy.
    # Here we simply return ack.
    return jsonify({'status': 'ok', 'received': payload})

# 新增统一反馈接口，持久化到feedbacks表
@app.route('/api/feedback', methods=['POST'])
def api_feedback():
    payload = request.get_json(force=True) or {}
    ts = datetime.now(timezone.utc).isoformat()
    adherence = int(payload.get('adherence') or 0)
    pain = int(payload.get('pain') or 0)
    duration_minutes = int(payload.get('duration_minutes') or 0)
    note = str(payload.get('note') or '')
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute(
        "INSERT INTO feedbacks (timestamp, adherence, pain, duration_minutes, note) VALUES (?, ?, ?, ?, ?)",
        (ts, adherence, pain, duration_minutes, note)
    )
    conn.commit()
    conn.close()
    return jsonify({'status': 'ok', 'timestamp': ts})

# 获取反馈列表
@app.route('/api/feedbacks', methods=['GET'])
def api_feedbacks():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT timestamp, adherence, pain, duration_minutes, note FROM feedbacks ORDER BY id DESC LIMIT 100")
    rows = c.fetchall()
    conn.close()
    items = [
        {
            'timestamp': r[0],
            'adherence': int(r[1] or 0),
            'pain': int(r[2] or 0),
            'duration_minutes': int(r[3] or 0),
            'note': r[4] or ''
        } for r in reversed(rows)
    ]
    return jsonify({'items': items})

# 种子测试数据（评估+计划+反馈）
@app.route('/api/mock/seed', methods=['POST'])
def api_mock_seed():
    import random
    now = datetime.now(timezone.utc)
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    # 生成评估与计划
    for i in range(20):
        ts = (now.replace(microsecond=0) - i*timedelta(minutes=10)).isoformat()
        # 随机分布的评分与异常概率
        score = max(0.0, min(100.0, random.gauss(75, 8)))
        anomaly_prob = max(0.0, min(1.0, random.gauss(0.15, 0.06)))
        c.execute("INSERT INTO evaluations (timestamp, score, anomaly_prob) VALUES (?, ?, ?)", (ts, float(round(score, 2)), float(round(anomaly_prob, 3))))
        plan = generate_plan(score, anomaly_prob)
        c.execute("INSERT INTO plans (timestamp, plan_json) VALUES (?, ?)", (ts, json.dumps(plan, ensure_ascii=False)))
    # 生成反馈
    for j in range(10):
        ts = (now.replace(microsecond=0) - j*timedelta(minutes=15)).isoformat()
        adherence = random.randint(60, 100)
        pain = random.randint(0, 6)
        duration_minutes = random.randint(15, 60)
        note = random.choice(['良好', '稍疲劳', '轻微酸胀', '完成度较高'])
        c.execute(
            "INSERT INTO feedbacks (timestamp, adherence, pain, duration_minutes, note) VALUES (?, ?, ?, ?, ?)",
            (ts, adherence, pain, duration_minutes, note)
        )
    conn.commit()
    conn.close()
    return jsonify({'status': 'ok', 'seeded_evaluations': 20, 'seeded_feedbacks': 10})


@app.route('/api/stats', methods=['GET'])
def api_stats():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT COUNT(*), AVG(score), AVG(anomaly_prob) FROM evaluations")
    row = c.fetchone()
    count = int(row[0] or 0) if row else 0
    avg_score = float(row[1] or 0.0) if row else 0.0
    avg_ap = float(row[2] or 0.0) if row else 0.0
    c.execute("SELECT timestamp, score, anomaly_prob FROM evaluations ORDER BY id DESC LIMIT 1")
    last = c.fetchone()
    conn.close()
    return jsonify({
        'count': count,
        'avg_score': round(avg_score, 2),
        'avg_anomaly_prob': round(avg_ap, 3),
        'last': (
            {'timestamp': last[0], 'score': float(last[1]), 'anomaly_prob': float(last[2])}
        ) if last else None
    })


@app.route('/api/plan/update', methods=['POST'])
def api_plan_update():
    payload = request.get_json(force=True) or {}
    plan = payload.get('plan') or payload
    ts = datetime.now(timezone.utc).isoformat()
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("INSERT INTO plans (timestamp, plan_json) VALUES (?, ?)", (ts, json.dumps(plan, ensure_ascii=False)))
    conn.commit()
    conn.close()
    return jsonify({'status': 'ok', 'timestamp': ts})


@app.route('/static/<path:path>')
def static_files(path):
    return send_from_directory(app.static_folder, path)


if __name__ == '__main__':
    os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
    init_db()
    app.run(host='127.0.0.1', port=5000, debug=True)